
using Progress.Lang.*.
using OpenEdge.WebSpeed.Container from propath.
using OpenEdge.WebSpeed.Request from propath.
using OpenEdge.WebSpeed.WebUtil from propath.
using OpenEdge.WebSpeed.WebContext from propath.

routine-level on error undo, throw.

class OpenEdge.WebSpeed.Response: 
        
    define stream web_stream.
    
    define public property WebContext as WebContext no-undo
        get.
        private set.

    /**
        return a handle to the web stream output stream
    */
    define public property WebStream as handle no-undo 
        get ():
			return stream web_stream:handle.
        end.
        private set.

    /**
      the content type of the response.  If not set this defaults to text/html
    */
    define public property ContentType as character no-undo 
        get.
        set.
    /**
      a reference to the initiating request object
    */
    define public property Request as Request no-undo
        get.
        private set.
    
    define private variable container     as Container no-undo.
    
    define private variable m_wseu-cookie as character no-undo.
        
    constructor public Response(container as Container, request as Request):
        this-object:Container = container.
        this-object:Request = request.
        this-object:WebContext = container:WebContext.
    end.
    
    destructor public Response():
        this-object:Container = ?.
        this-object:Request = ?.
        this-object:WebContext = ?.
    end.
        
    /**
      generic write method to write a chunk of text out to the web stream
    */
    method public void Write(out as character):
        put stream web_stream unformatted out.
    end.
    
    /**
      export the entire longchar out to the web stream
    */
    method public void Write(out as longchar):
        export stream web_stream out.
    end.
    
    /**
      bulk method to export the entire contents of a memptr out to the web stream
    */
    method public void Write(out as memptr):
        export stream web_stream out.
    end.
    
    
    /**
      Open the web output stream.  
          
      NOTE: this is a framework method and should not be called from your program.
        
        
    */
    method public void OpenWebStream():
        output stream web_stream to "WEB":U.
    end.
    
    /**
      Close the web output stream.
      
      NOTE: this is a framework method and should not be called from your program.
    */
    
    method public void CloseWebStream():
        output stream web_stream close.
    end.
    
    /**
        
      closes the webstream and cleans up the response to the client.
        
    */
    method public void EndResponse():
        this-object:CloseWebStream().
    end.
    
    /**
        
      opens the webstream and initializes the response to the client.
    */
    method public void InitResponse():
        this-object:OpenWebStream().
    
    end.
    
    /**
      Deletes the cookie specified by the given name, path, and domain
      
    */
    method public character DeleteCookie(
        p_name as character,
        p_path as character,
        p_domain as character):
        return this-object:SetCookie (p_name, "", today - 1, 0, ?, ?, ?).
    end.

    method public character CookieDate(
        p_date    as date,
        p_time    as integer):
          
        /* If date is ?, return a blank date */
        if p_date = ? then 
        do:
            return "".
        end.

        /* Return a formatted date/time based on date and time that are local. */
        return this-object:Request:format-datetime("cookie":U, p_date, p_time, "local":U).
    
    end.

    /**
      set a cookie that will be sent back to the requesting client when the request ends
	
    */    
    method public character SetCookie
        (p_name   as character,
        p_value  as character,
        p_date   as date,
        p_time   as integer,
        p_path   as character,
        p_domain as character,
        p_options as character):

        define variable exp-date   as character no-undo.
        define variable v-cookie   as character no-undo.
        define variable v-secure   as character no-undo.
        define variable v-timeconv as character no-undo.

        /* Nothing to do if no Cookie name */
        if p_name = ? or p_name = "" then return "".

        if p_value = ? then
            p_value = "".
        if p_path = ? then
            p_path = container:CookiePath.
        if p_domain = ? then
            p_domain = container:CookieDomain.
        if p_options = ? then
            p_options = "".

        /* If any options were specified, check for "secure" or "UTC" */
        if p_options <> "" then 
        do:
            /* Look for "SECURE" in options */
            v-secure = (if can-do(p_options, "SECURE":U) then "SECURE":U else "").
            /* Look for "UTC" in options. If not found, assume local time */
            v-timeconv = (if can-do(p_options, "UTC":U) then "UTC":U else "LOCAL":U).
        end.

        /* If an expiration date/time was specified, convert date and time to
           a modified RFC-1123 format for output. */
        if p_date <> ? then 
        do:
            exp-date = this-object:Request:format-datetime("cookie":U, p_date, p_time, v-timeconv).
            /* Set path to "/" to work around a Netscape 1.1 and earlier bug
               with expires.  This test might not be adequate.*/
        
            if this-object:Request:HTTPUserAgent begins "Mozilla/1.1":U then
                p_path = "/":U.
        end.
        /* No date specified */
        else
            exp-date = "".

        /* Format the cookie */
        assign 
            v-cookie = WebUtil:url-encode(p_name,"cookie":U) + "=":U +
            WebUtil:url-encode(p_value,"cookie":U) +
            (if exp-date = "" then "" else "~; expires=":U + exp-date) +
            (if p_path = "" then "" else "~; path=":U + p_path) +
            (if p_domain = "" then "" else "~; domain=":U + p_domain) +
            (if v-secure = "" then "" else "~; secure":U).

        /* Send the cookie to the web browser */
        this-object:output-http-header("Set-Cookie":U, v-cookie).
  
        return v-cookie.
    
    end.
    
    /**
     Outputs the specified HTTP header with associated value followed
      by a carriage return and linefeed.  If the header name is blank, then the
      value and carriage return/linefeed pair are still output.
    Input Parameters: HTTP Header name (less colon), associated header value.
    
    */    
    method public character output-http-header
        (p_header as character,
        p_value  as character) :
  
        /* Remove a trailing colon or spaces from the header name.  Add it back
           on exactly the way we want it. */
         
        p_header = right-trim(p_header, ": ":U).
        if p_header <> "" then
            p_header = p_header + ": ":U.

        /* Output the header and associated value to the output stream */
        put stream web_stream control
            p_header
            p_value 
            this-object:Request:HTTPNewLine.
    end.  
    
    
    method public logical ShowErrorScreen( e as Progress.Lang.Error ):
    
        if file-info:full-pathname ne ? then 
            run value(file-info:full-pathname) (e:GetMessage(1)) no-error.
        else 
        do:
            output-content-type("text/html":U).
            this-object:Write("<BR>":U + e:GetMessage(1) + "<BR>":U).
            this-object:Write("<BR>":U + e:CallStack + "<BR>":U).
        end.
    end.
    
    /**
      
      output a generic error message to the web stream
      
    */  
    method public logical ShowErrorScreen 
        (  cErrorMsg as character) :
        define variable iCntr   as integer   no-undo.
        define variable cTxt    as character no-undo.
        define variable lRetVal as logical   no-undo.

        /* Check to see if there are any errors. If so, output them one by one. */
        if error-status:error             and 
            error-status:num-messages gt 0 then 
        do:
            cErrorMsg = cErrorMsg + "~n<br><H1>Error Messages</H1>~n~n".

            do iCntr = 1 to error-status:num-messages:
                cErrorMsg = cErrorMsg +
                    "<P>":U + html-encode(error-status:get-message(iCntr)) + 
                    "</P>~n":U no-error.
            /*                DYNAMIC-FUNCTION ("LogNote":U IN web-utilities-hdl, "Note":U,*/
            /*                    STRING (" " + ERROR-STATUS:GET-MESSAGE(iCntr) + "")).    */
            end. /* DO cntr... */
        end. /* IF...NUM-MESSAGES > 0 */
    
        /* Custom error file... this should be enhanced to handle specific errors
           such as database connectivity, redirects, run-time problems,  access denial
           and other similar things.  This would mean another input parameter would
           need to be added for error type. */
        file-info:file-name = search(this-object:Container:AgentSettings:GetAgentSetting("Misc":U, "", "ErrorProc":U)) no-error.
    
        if file-info:full-pathname ne ? then 
            run value(file-info:full-pathname) (cErrorMsg) no-error.
        else 
        do:
            output-content-type("text/html":U).
            this-object:Write("<BR>":U + cErrorMsg + "<BR>":U).
        end.

    end. 

    /**
      
      Sets and outputs the MIME Content-Type header followed by a
      blank line.  If the header was already output, no action is taken.
    
    */    
    method public logical output-content-type 
        (  p_type as character ) :
        define variable cMimeCharset as character no-undo.
   
        if this-object:ContentType eq "" then 
        do:
            this-object:ContentType = (if p_type = "" then ? else p_type).
      
            /* Add MIME codepage, if available. */
            if this-object:ContentType begins trim("text/html":U) 
                and index(this-object:ContentType, "charset":U) = 0
                and this-object:WebContext:html-charset <> "" then 
            do:
                run adecomm/convcp.p ( this-object:WebContext:html-charset, "toMime":U,
                    output cMimeCharset ) no-error.
                if cMimeCharset <> "" then
                    this-object:ContentType = this-object:ContentType    + "; charset=":U + 
                        cMimeCharset.
            end.
      
            if this-object:ContentType ne ? then
                output-http-header ("Content-Type":U, this-object:ContentType).
    
            /* This is required as a 'delimiter' for the header information */
            output-http-header ("":U, "":U).  /* blank line */

            /* If output-content-type is not ?, then a Content-Type header was output so return TRUE. */
            return (this-object:ContentType ne ?).
        end. /* output-content-type eq "" */

    end.

    /**
     Converts various ASCII characters to their HTML representation
      to prevent problems with invalid HTML.  This procedure can only be called
      once on a string or ampersands will incorrectly be replaced with "&amp; .
      
      Input Parameter: Character string to encode
      Returns: Encoded character string
    
    */    
    method public character html-encode
        (p_in as character):
        /* Ampersand must be replaced first or the output will be hosed if done
           after any of these other subsititutions. */
        assign
            p_in = replace(p_in, "&":U, "&amp~;":U) 
            p_in = replace(p_in, "~"":U, "&quot~;":U)
            p_in = replace(p_in, "<":U, "&lt~;":U)
            p_in = replace(p_in, ">":U, "&gt~;":U). 
        return p_in.
    end.
    
	
	
    /**
	
     Sets the WSEU cookie in a standard way.  This also changes
      the wseu-cookie variable to the value set.  When this is called, it checks
      the current value of wseu-cookie, and only sets the new value if it is
      different.  If the new value, p_cookie, is blank or ?, then the wseu
      cookie is deleted.
    Input Parameters: p_cookie -- the new cookie value..
	
    */
	
    method public character SetWSEUCookie 
        (    p_cookie as character) :
        /* Change unknown to blank. */
        if p_cookie eq ? then    
            p_cookie = "".
	
        /* Is the cookie value different? */
        if p_cookie ne m_wseu-cookie then 
        do:
            /* Save the new value. */
            m_wseu-cookie = p_cookie.
            if p_cookie eq "" then 
                return DeleteCookie ("WSEU":U, ?, ?).   
            else 
            do:
                if not this-object:Container:EvaluationMode then
                    return SetCookie ("WSEU":U, m_wseu-cookie, ?, ?, ?, ?, ?).    
            end.
        end.  
    end.

end class.